package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.config.redis.RedissonConfig;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.login.GuiGuLogin;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.atguigu.tingshu.common.constant.SystemConstant.*;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;

    @Autowired
    private AlbumStatMapper albumStatMapper;

    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private AuditServiceImpl auditService;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAlbumInfo(AlbumInfoVo albumInfoVo, Long userId) {

        //保存专辑信息
        //将专辑VO转为PO对象
        AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
        //为专辑属性赋值：用户id、试听集数、审核状态
        albumInfo.setUserId(userId);
        String payType = albumInfo.getPayType();
        //只为VIP免费或者付费资源设置试听集数
        if (ALBUM_PAY_TYPE_VIPFREE.equals(payType) || ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
            albumInfo.setTracksForFree(albumInfoVo.getTracksForFree());
        }
        albumInfo.setStatus(ALBUM_STATUS_NO_PASS);
        //保存专辑，得到专辑id
        albumInfoMapper.insert(albumInfo);
        Long albumId = albumInfo.getId();
        //2.保存专辑标签关系信息
        //2.1 获取VO中提交专辑标签关系集合
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (CollUtil.isNotEmpty(albumAttributeValueVoList)) {
            //2.2 为专辑标签关联专辑ID，"批量"新增专辑标签关系
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                AlbumAttributeValue albumAttributeValue = BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class);
                albumAttributeValue.setAlbumId(albumId);
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }

        //3.初始化专辑统计信息
        this.saveAlbumInfoStat(albumId, ALBUM_STAT_PLAY, 0);
        this.saveAlbumInfoStat(albumId, ALBUM_STAT_SUBSCRIBE, 0);
        this.saveAlbumInfoStat(albumId, ALBUM_STAT_BUY, 0);
        this.saveAlbumInfoStat(albumId, ALBUM_STAT_COMMENT, 0);

        //4.对专辑中文本内容进行审核  & 索引库ES中新增记录
        String text = albumInfo.getAlbumTitle() + albumInfo.getAlbumIntro();
        String suggestion = auditService.auditText(text);
        if ("pass".equals(suggestion)) {
            albumInfo.setStatus(ALBUM_STATUS_PASS);
            //TODO 发送MQ消息 通知 搜索服务 将专辑存入ES引库
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumId);
        } else if ("review".equals(suggestion)) {
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_REVIEW);
        } else if ("block".equals(suggestion)) {
            albumInfo.setStatus(ALBUM_STATUS_NO_PASS);
        }
        albumInfoMapper.updateById(albumInfo);
    }

    @Override
    public void saveAlbumInfoStat(Long albumId, String statType, int statNum) {
        AlbumStat albumStat = new AlbumStat();
        albumStat.setAlbumId(albumId);
        albumStat.setStatType(statType);
        albumStat.setStatNum(statNum);
        albumStatMapper.insert(albumStat);
    }

    @Override
    public Page<AlbumListVo> findUserAlbumPage(Page<AlbumListVo> pageParam, AlbumInfoQuery albumInfoQuery) {
        return albumInfoMapper.findUserAlbumPage(pageParam, albumInfoQuery);
    }

    @Override
    public void removeAlbumInfo(Long id) {

        //1.删除专辑,查数量就行了，有就不能删
        Long count = trackInfoMapper.selectCount(
                new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getAlbumId, id)
        );
        if (count > 0) {
            throw new GuiguException(500, "该专辑下存在关联信息不允许删除");
        }
        //2.删除专辑标签关系
        albumInfoMapper.deleteById(id);
        //3.删除专辑统计信息
        albumStatMapper.delete(
                new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId, id)
        );
        //TODO 4.删除一拉色吃中的缓存数据
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, id);
    }

    @Override
    public AlbumInfo getAlbumInfoFromDB(Long id) {
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);
        LambdaQueryWrapper<AlbumAttributeValue> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AlbumAttributeValue::getAlbumId, id);
        List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueMapper.selectList(queryWrapper);
        if (CollUtil.isNotEmpty(albumAttributeValues)) {
            albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
        }
        return albumInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo) {
        AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
        albumInfoMapper.updateById(albumInfo);
        albumInfo.setId(id);
        albumInfo.setStatus(ALBUM_STATUS_NO_PASS);
        albumInfoMapper.updateById(albumInfo);
        //2.更新专辑标签关系
        //2.1 根据专辑ID删除原有标签关系
        albumAttributeValueMapper.delete(
                new LambdaQueryWrapper<AlbumAttributeValue>()
                        .eq(AlbumAttributeValue::getAlbumId, id)
        );
        //2.2 从VO中获取提交专辑标签关系集合 再次批量保存
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (CollUtil.isNotEmpty(albumAttributeValueVoList)) {
            //为专辑标签关联专辑ID，"批量"新增专辑标签关系
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                AlbumAttributeValue albumAttributeValue = BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class);
                albumAttributeValue.setAlbumId(id);
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }
        //3.再次对内容进行审核
        String text = albumInfo.getAlbumTitle() + albumInfo.getAlbumIntro();
        String suggestion = auditService.auditText(text);
        if ("pass".equals(suggestion)) {
            albumInfo.setStatus(ALBUM_STATUS_PASS);
            //TODO 发送MQ消息 通知 搜索服务 将专辑存入ES引库
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, id);
        } else if ("review".equals(suggestion)) {
            albumInfo.setStatus(ALBUM_STATUS_REVIEW);
            //TODO 发送MQ消息 通知 搜索服务 从ES引库删除
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, id);
        } else if ("block".equals(suggestion)) {
            albumInfo.setStatus(ALBUM_STATUS_NO_PASS);
            //TODO 发送MQ消息 通知 搜索服务 从ES引库删除
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, id);
        }
        albumInfoMapper.updateById(albumInfo);
    }

    @Override
    public List<AlbumInfo> findUserAllAlbumList(Long userId) {
        //1.构建查询条件QueryWrapper对象
        LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();
        //1.1 查询条件
        queryWrapper.eq(AlbumInfo::getUserId, userId);
        //1.2 排序
        queryWrapper.orderByDesc(AlbumInfo::getId);
        //1.3 限制记录数
        queryWrapper.last("LIMIT 200");
        //1.4 指定查询列
        queryWrapper.select(AlbumInfo::getId, AlbumInfo::getAlbumTitle);
        //2.执行列表查询
        return albumInfoMapper.selectList(queryWrapper);
    }

    @Override
    public AlbumStatVo getAlbumStatVo(Long albumId) {
        return albumInfoMapper.getAlbumStatVo(albumId);
    }

    /**
     * 查询专辑信息
     * 采用Redisson分布式锁解决缓存击穿问题
     *
     * @param id
     * @return
     */
    @Override
    public AlbumInfo getAlbumInfo(Long id) {
        try {
            //1.优先从缓存Redis中获取业务数据，如果命中缓存直接返回；
            //1.1 构建专辑信息业务数据Key 形式：前缀+专辑ID
            String dataKey = RedisConstant.ALBUM_INFO_PREFIX + id;
            //1.2 查询Redis获取专辑业务数据，命中则响应
            AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(dataKey);
            if (albumInfo != null) {
                return albumInfo;
            }
            //2.获取分布式锁：基于Redisson框架提供分布式锁
            //2.1 构建专辑信息锁的Key 形式=业务key+锁后缀
            String lockKey = dataKey + RedisConstant.CACHE_LOCK_SUFFIX;
            //2.1 尝试获取分布式锁
            RLock lock = redissonClient.getLock(lockKey);
            // 锁释放时间：压测：取99压测结果（99%请求在多少秒响应）
            boolean flag = lock.tryLock(1,1, TimeUnit.SECONDS);

            //3.获取锁成功，执行查库业务，将结果再次放入Redis缓存中返回，释放锁
            if (flag) {
                try {
                    //3.1 执行查库业务
                    albumInfo = this.getAlbumInfoFromDB(id);
                    //3.2 将查询结果放入Redis中
                    long ttl = RedisConstant.ALBUM_TIMEOUT + RandomUtil.randomInt(1200);
                    redisTemplate.opsForValue().set(dataKey, albumInfo, ttl, TimeUnit.SECONDS);
                    return albumInfo;
                } finally {
                    //3.3 释放锁
                    lock.unlock();
                }
            } else {
                //4.获取锁失败，自旋，可能就会命中缓存
                Thread.sleep(500);
                this.getAlbumInfo(id);
            }
        } catch (Exception e) {
            log.error("Redis服务异常：{}", e.getMessage());
            //5.兜底处理：如果Redis服务不可用，则从数据库中查询数据
            return this.getAlbumInfoFromDB(id);
        }
        return null;
    }
}

